package com.wiscamp.ninechapters.panels.infrastructure.repositories;


import com.wiscamp.ninechapters.panels.domain.entities.Group;
import com.wiscamp.ninechapters.panels.domain.repositories.GroupRepository;
import com.wiscamp.ninechapters.panels.infrastructure.converters.GroupConverter;
import com.wiscamp.ninechapters.panels.infrastructure.mappers.GroupMapper;
import com.wiscamp.ninechapters.panels.infrastructure.mappers.GroupRoleMapper;
import com.wiscamp.ninechapters.panels.infrastructure.mappers.GroupUserMapper;
import com.wiscamp.ninechapters.panels.infrastructure.mappers.GroupViewMapper;
import com.wiscamp.ninechapters.panels.infrastructure.po.GroupPO;
import com.wiscamp.ninechapters.panels.infrastructure.po.GroupRolePO;
import com.wiscamp.ninechapters.panels.infrastructure.po.GroupUserPO;
import com.wiscamp.ninechapters.panels.infrastructure.views.GroupView;
import cube.ddd.aspect.HandleDataException;
import cube.ddd.common.query.PagedObjects;
import cube.ddd.common.query.PagedQuery;
import cube.ddd.utils.PersistenceUtils;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.Objects;

@Repository
public class GroupRepositoryImpl implements GroupRepository {

    private final GroupMapper groupMapper;

    private final GroupViewMapper groupViewMapper;

    private final GroupUserMapper groupUserMapper;

    private final GroupRoleMapper groupRoleMapper;

    public GroupRepositoryImpl(GroupMapper groupMapper, GroupViewMapper groupViewMapper, GroupUserMapper groupUserMapper, GroupRoleMapper groupRoleMapper) {
        this.groupMapper = groupMapper;
        this.groupViewMapper = groupViewMapper;
        this.groupUserMapper = groupUserMapper;
        this.groupRoleMapper = groupRoleMapper;
    }

    private void loadDetails(Group group) {
        if (Objects.nonNull(group)) {
            var userIds = groupUserMapper.findUserIdsByGroup(group.getGroupId());
            var roleIds = groupRoleMapper.findRoleIdsByGroup(group.getGroupId());
            group.setUserIds(userIds);
            group.setRoleIds(roleIds);
        }
    }

    private GroupPO getGroupPO(long groupId) {
        return PersistenceUtils.findById(groupId, groupMapper::findById);
    }

    @HandleDataException
    public Group getGroup(long groupId) {
        var group = PersistenceUtils.findEntityById(groupId, groupViewMapper::findById,
                GroupConverter.Instance::toGroupByView);
        loadDetails(group);
        return group;
    }

    @HandleDataException
    public Group getGroupByCode(long appId, String groupCode) {
        var group = PersistenceUtils.getEntityByIdAndText(appId, groupCode, groupViewMapper::findByGroupCode,
                GroupConverter.Instance::toGroupByView);
        loadDetails(group);
        return group;
    }

    @HandleDataException
    public Collection<Group> getGroupsByName(long appId, String groupName) {
        var groups = PersistenceUtils.getEntitiesByIdAndText(appId, groupName, groupViewMapper::findListByGroupName,
                GroupConverter.Instance::toGroupByView);
        for (var group : groups) {
            loadDetails(group);
        }
        return groups;
    }

    @HandleDataException
    public Collection<Group> getGroupsByIds(Collection<Long> groupIds) {
        var groups = PersistenceUtils.getEntitiesByIds(groupIds, groupViewMapper::findAllById,
                GroupConverter.Instance::toGroupByView);
        for (var group : groups) {
            loadDetails(group);
        }
        return groups;
    }

    @HandleDataException
    public PagedObjects<Group> getGroupsByQuery(PagedQuery query) {
        PagedObjects<GroupView> pagedObjects = PersistenceUtils.getPersistenceObjectsByQuery(query, groupViewMapper::findAll);
        return PersistenceUtils.convertPagedObjects(pagedObjects, GroupConverter.Instance::toGroupByView);
    }

    @HandleDataException
    public Group saveGroup(Group group) {
        if (group.getGroupId() <= 0) {
            return PersistenceUtils.createEntity(group, groupMapper::saveAndFlush,
                    GroupConverter.Instance::toGroupPO, GroupConverter.Instance::toGroup);
        } else {
            var existedPO = getGroupPO(group.getGroupId());
            if (existedPO == null) existedPO = new GroupPO();
            return PersistenceUtils.updateEntity(group, existedPO, groupMapper::saveAndFlush,
                    GroupConverter.Instance::toGroupPO, GroupConverter.Instance::toGroup);
        }
    }

    @HandleDataException
    public boolean bindGroupRole(long groupId, long roleId) {
        var groupRole = groupRoleMapper.findByGroupAndRole(groupId, roleId);
        if (groupRole == null) {
            groupRole = new GroupRolePO();
            groupRole.setGroupId(groupId);
            groupRole.setRoleId(roleId);
            groupRoleMapper.saveAndFlush(groupRole);
        }
        return true;
    }

    @HandleDataException
    public boolean unbindGroupRole(long groupId, long roleId) {
        var groupRole = groupRoleMapper.findByGroupAndRole(groupId, roleId);
        if (groupRole != null) {
            groupRoleMapper.delete(groupRole);
        }
        return true;
    }

    @HandleDataException
    public boolean bindGroupUser(long groupId, long userId) {
        var groupUser = groupUserMapper.findByGroupAndUser(groupId, userId);
        if (groupUser == null) {
            groupUser = new GroupUserPO();
            groupUser.setGroupId(groupId);
            groupUser.setUserId(userId);
            groupUserMapper.saveAndFlush(groupUser);
        }
        return true;
    }

    @HandleDataException
    public boolean unbindGroupUser(long groupId, long userId) {
        var groupUser = groupUserMapper.findByGroupAndUser(groupId, userId);
        if (groupUser != null) {
            groupUserMapper.delete(groupUser);
        }
        return true;
    }
}
